package com.hhd.pdf.components.table.factory;

import com.hhd.pdf.CommonUtils;
import com.hhd.pdf.components.table.TableBorderHelper;
import com.hhd.pdf.components.table.TableVo;
import com.hhd.pdf.components.table.TdVo;
import com.hhd.pdf.components.table.TrVo;
import com.hhd.pdf.context.GeneratorContext;
import com.hhd.pdf.utils.PdfUtils;
import com.hhd.pdf.variable.CompositeFormula;
import com.hhd.pdf.variable.ParsedFormula;
import com.hhd.pdf.variable.VariableContext;
import com.hhd.pdf.variable.VariablePath;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Element;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.Phrase;
import com.itextpdf.text.pdf.PdfDiv;
import com.itextpdf.text.pdf.PdfPCell;
import com.itextpdf.text.pdf.PdfPTable;
import lombok.Builder;
import org.dom4j.Attribute;

import java.io.IOException;
import java.util.List;

public class TableBodyFactory extends TableHeaderFactory {

    @Builder(builderMethodName = "bodyBuilder")
    TableBodyFactory(PdfPTable pdfPTable, GeneratorContext context, TableVo tableVo, TableBorderHelper tableBorderHelper) {
        super(pdfPTable, context, tableVo, tableBorderHelper);
    }

    @Override
    public void create() throws DocumentException, IOException {
        List<TrVo> body = tableVo.getBody();
        if (CommonUtils.isEmpty(body)) return;

        doCreate(body);
    }

    void doCreate(List<TrVo> trs) throws DocumentException, IOException {
        deepCreateTrs(reateRootVariableContext(), trs, 0);
    }

    private void deepCreateTrs(List<VariableContext> variableContexts,
                               List<TrVo> trs, int rowIndex) throws DocumentException, IOException {
        if (rowIndex >= trs.size()) return;

        for (VariableContext variableContext : variableContexts) {
            TrVo tr = trs.get(rowIndex);
            if (!has(tr.getTr(), variableContext)) {
                deepCreateTrs(variableContexts, trs, rowIndex + 1);
                continue;
            }
            List<ParsedFormula> trFormulas = getTrFormulas(tr);

            if (CommonUtils.isEmpty(trFormulas)) {
                createRow(variableContext, tr);
                deepCreateTrs(CommonUtils.newList(variableContext), trs, rowIndex + 1);
//                deepCreateTrs(variableContexts, trs, rowIndex + 1);
            } else {
                ParsedFormula formula = getMaxDeepFormula(trFormulas, tr);
                if (rowIndex > 0) {
                    List<VariableContext> childContexts = getChildContexts(variableContext, formula, tr);
                    for (VariableContext childContext : childContexts) {
                        createRow(childContext, tr);
                    }
//                    createRow(variableContext, tr);
                    deepCreateTrs(CommonUtils.newList(variableContext), trs, rowIndex + 1);
                    return;
                }
                List<VariableContext> childContexts = getChildContexts(variableContext, formula, tr);
                for (VariableContext childContext : childContexts) {
                    createRow(childContext, tr);
                    deepCreateTrs(CommonUtils.newList(childContext), trs, rowIndex + 1);
                }
            }
        }
    }

    private List<VariableContext> getChildContexts(VariableContext variableContext, ParsedFormula formula, TrVo tr) {
        if (CommonUtils.isEmpty(tr.getTr().attributeValue("list"))) {
            return variableContext.subContextByFormula(formula);
        }
        return variableContext.subContextByFormula(formula, false);
    }

    private void createRow(VariableContext variableContext,
                           TrVo tr) throws DocumentException, IOException {
        for (TdVo td : tr.getTds()) {
            PdfPCell pdfPCell = buildCell(td, tableBorderHelper, tableVo, getContext());
            pdfPCell.addElement(wrapElements(createPdfElementsByVariableContext(variableContext, td.getTd(), getContext()), td, tr));

            resolveHeaderType(tr, pdfPCell);

            pdfPTable.addCell(pdfPCell);
            tableBorderHelper.nextCol();
        }
        tableBorderHelper.nextRow();
    }

    private void resolveHeaderType(TrVo tr, PdfPCell pdfPCell) {
        try {
            if (tr.getTr().attribute("type").getText().equals("header")) {
                pdfPCell.setBackgroundColor(PdfUtils.createColor(250, 250, 250));
            }
        } catch (Exception e) {
        }
    }

    private Element wrapElements(List<Element> elements, TdVo td, TrVo tr) {
        Element paragraph;
        if ((elements.size() == 1 && PdfUtils.isPhrase(elements.get(0))) || isAllPhrase(elements)) {
            paragraph = createParagraph(elements, td);
        } else {
            paragraph = createPdfDiv(elements);
        }
        return paragraph;
    }

    private boolean isAllPhrase(List<Element> elements) {
        for (Element element : elements) {
            if(!PdfUtils.isPhrase(element)) return false;
        }
        return true;
    }

    private Element createParagraph(List<Element> elements, TdVo td) {
        Paragraph paragraph = PdfUtils.createParagraph();
        resolveAligh(paragraph, td);
        for (Element element : elements) {
            resolveFont(element);
            paragraph.add(element);
        }
        return paragraph;
    }

    private PdfDiv createPdfDiv(List<Element> elements) {
        PdfDiv paragraph = PdfUtils.createDiv(0, 0);
        for (Element element : elements) {
            resolveFont(element);
            paragraph.addElement(element);
        }
        return paragraph;
    }

    private void resolveFont(Element element) {
        if (PdfUtils.isPhrase(element)) {
            Phrase phrase = PdfUtils.toPhrase(element);
            phrase.setFont(context.getGeneratorParameters().getFont());
        }
    }

    private static void resolveAligh(Paragraph paragraph, TdVo td) {
        paragraph.setAlignment(Paragraph.ALIGN_CENTER);
        try {
            org.dom4j.Element td1 = td.getTd();
            Attribute attribute = td1.attribute("align");
            if ("left".equals(attribute.getText())) {
                paragraph.setAlignment(Element.ALIGN_LEFT);
            }

            if ("right".equals(attribute.getText())) {
                paragraph.setAlignment(Element.ALIGN_RIGHT);
            }
        } catch (Exception e) {
        }
    }

    private static Paragraph getParagraph(Element element, TdVo td) {
        Paragraph paragraph = PdfUtils.createParagraph();
        paragraph.setAlignment(td.getAlign());
        paragraph.add(element);
        return paragraph;
    }

    private List<ParsedFormula> getTrFormulas(TrVo tr) {
//        List<TdVo> tds = tr.getTds();
//        LinkedList<ParsedFormula> res = new LinkedList<>();
//        tds.stream().map(TdVo::getTd)
//                .map(this::getFormulas)
//                .forEach(res::addAll);
        CompositeFormula formula = CompositeFormula.formula(tr.getTr().asXML());
        return formula.getFormulas();
    }


    public List<VariableContext> reateRootVariableContext() {
        return CommonUtils.newList(
                VariableContext.builder()
                        .container(getContext().getGeneratorParameters().getRenderData())
                        .basePath(VariablePath.root())
                        .build());
    }
}
